// build:20240626
// This component allows you to configure the network and monitor network status changes.
// Dependencies: dxMap, dxLogger, dxDriver, dxEventBus
import dxMap from './dxMap.js'
import bus from './dxEventBus.js'
import { netClass } from './libvbar-m-dxnet.so'
const netObj = new netClass();
const map = dxMap.get("default")

const net = {}
net.TYPE = {
    "UNKNOWN": 0,
    "ETHERNET": 1,
    "WIFI": 2,
    "4G": 4
}
net.DHCP = {
    STATIC: 1,
    DYNAMIC: 2,
    WIFI_AP: 3 // WiFi AP Hotspot Mode
}

/**
 * Network initialization (Wi-Fi or Ethernet) will automatically retry if a connection fails, eliminating the need for repeated `init` steps. However, after `init`, it's necessary to poll for network status (using `msgReceive`).
 * Alternatively, you can directly use the simplified method dxNet.run, which eliminates the need for polling.
 * @param {object} options Initialize network parameters
 *       @param {number} type Required field: Network type. Refer to the net.TYPE enumeration.
 *       @param {number} dhcp DHCP is required; refer to the net.DHCP enumeration.
 *       @param {string} macAddr Required field: MAC address. The default method used is dxCommon.getUuid2mac() to obtain the MAC address.
 *       @param {string} ip Optional field: Network IP address
 *       @param {string} gateway Optional: Gateway address
 *       @param {string} netmask Optional subnet mask
 *       @param {string} dns0 Optional DNS address
 *       @param {string} dns1 Optional DNS address
 * @returns 
 */
net.init = function (options) {
    let ret = netObj.init()
    if (!ret) {
        return false
    }
    if (!options) {
        throw new Error("dxNet.init: 'options' parameter should not be null or empty")
    }
    ret = netObj.setMasterCard(options.type)
    if (!ret) {
        return false
    }
    netObj.setMacaddr(options.type, options.macAddr)
    ret = netObj.cardEnable(options.type, true)
    if (!ret) {
        return false
    }
    if (options.dhcp === 1) {
        return netObj.setModeByCard(options.type, 1, {
            ip: options.ip,
            gateway: options.gateway,
            netmask: options.netmask,
            dns0: options.dns0,
            dns1: options.dns1,
        })
    } else if (options.dhcp === 2) {
        return netObj.setModeByCard(options.type, options.dhcp)
    }
    return false
}

/**
 * Get MAC address
 * @param {number} type  Required field: Network type. Refer to the net.TYPE enumeration.
 * @returns   Mac address
 */
net.getMacaddr = function (type) {
    return netObj.getMacaddr(type)
}
/**
 * Set MAC address
 * @param {number} type  Required field: Network type. Refer to the net.TYPE enumeration.
 * @param {string} addr  MAC address, required, format similar to b2:a1:63:3f:99:b6
 * @returns   true: Success (main network interface type); false: Failure
 */
net.setMacaddr = function (type, addr) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.setMacaddr:'type' paramter should not be null or empty")
    }
    if (addr === null || addr === undefined || addr.length < 1) {
        throw new Error("dxNet.setMacaddr:'addr' paramter should not be null or empty")
    }
    return netObj.setMacaddr(type, addr)
}
/**
 * Enable the network interface card and add it to the network management module.
 * @param {number} type  Required field: Network type. Refer to the net.TYPE enumeration.
 * @param {boolean} on  On/Off
 * @returns   0: Success; <0: Failure
 */
net.cardEnable = function (type, on) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.cardEnable: 'type' parameter should not be null or empty")
    }
    if (on === null) {
        throw new Error("dxNet.cardEnable: 'on' parameter should not be null or empty")
    }
    return netObj.cardEnable(type, on)
}
/**
 * net network destruction
 * @return true: success, false: failure
 */
net.exit = function () {
    return netObj.exit()
}
/**
 * Configure the mode and corresponding parameters of the specified network interface card (NIC).
 * @param {number} type   Required field: Network type. Refer to the net.TYPE enumeration.
 * @param {number} mode   DHCP is required; refer to the net.DHCP enumeration.
 * @param param  Network parameters
 * @return true: success, false: failure
 */
net.setModeByCard = function (type, mode, param) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.setModeByCard: 'type' parameter should not be null or empty")
    }
    if (mode === null) {
        throw new Error("dxNet.setModeByCard:'mode' parameter should not be null or empty")
    }
    return netObj.setModeByCard(type, mode, param)
}
/**
 * Get the mode and corresponding parameters of a specified network interface card (NIC).
 * @param {number} type  Required field: Network type. Refer to the net.TYPE enumeration.
 * @returns   If it's a static network, it will return information such as IP address and gateway.
 */
net.getModeByCard = function (type) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.getModeByCard: 'type' parameter should not be null or empty")
    }

    return netObj.getModeByCard(type)
}
/**
 * Configure the primary network interface card (NIC), and let the application's network status be determined by the secondary NIC.
 * @param {number} type  Required field: Network type. Refer to the net.TYPE enumeration.
 * @returns    true: success, false: failure
 */
net.setMasterCard = function (type) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.setMasterCard: 'type' parameter should not be null or empty")
    }
    return netObj.setMasterCard(type)
}
/**
 * Obtain the main network card
 * @returns   >0: Success (main network interface type); <0: Failure
 */
net.getMasterCard = function () {
    return netObj.getMasterCard()
}
/**
 * Retrieving network status is similar to {"status":4, "connected":true}, where status is as follows:
 *  0,    Uninitialized state
    1,    The network card is in the off state.
    2,    The network card is in the enabled state.
    3,    The network cable is plugged in or the Wi-Fi is connected via SSID, but no IP address has been assigned.
    4,    IP address successfully assigned
    5     The specified service has been connected or the test has been passed to connect to the wide area network.
 * @returns   Network status
 */
net.getStatus = function () {
    let status = netObj.getStatus()
    return { "status": status, "connected": status >= 4 }
}
/**
 * Configure network status
 * @param {number} status Network status (required)
 * @returns true: success, false: failure
 */
net.setStatus = function (status) {
    if (status === null || status === undefined) {
        throw new Error("dxNet.setStatus: 'status' parameter should not be null or empty")
    }
    return netObj.setStatus(status)
}

/**
 * Re-enable the network card
 * @param {number} type         Network type (required)
 * @param {number} phy_reset    On/Off (Required)
 * @returns true: success, false: failure
 */
net.netCardReset = function (type, phy_reset) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.setStatus: 'status' parameter should not be null or empty")
    }
    if (phy_reset === null || phy_reset === undefined) {
        throw new Error("dxNet.setStatus: 'status' parameter should not be null or empty")
    }
    return netObj.netCardReset(type, phy_reset)
}

/**
 * Get WiFi list
 * @param {*} timeout Required
 * @param {*} interval Required
 * @returns WiFi list
 */
net.netGetWifiSsidList = function (timeout, interval) {
    if (timeout === null || timeout === undefined) {
        throw new Error("dxNet.netGetWifiSsidList: 'timeout' parameter should not be null or empty")
    }
    if (interval === null) {
        throw new Error("dxNet.netGetWifiSsidList: 'interval' parameter should not be null or empty")
    }
    return netObj.netGetWifiSsidList(timeout, interval)
}
/**
 * Connect to Wi-Fi
 * @param {*} ssid Required
 * @param {*} psk Required
 * @param {*} params Required
 * @returns 
 */
net.netConnectWifiSsid = function (ssid, psk, params) {
    if (ssid === null) {
        throw new Error("dxNet.netConnectWifiSsid: 'ssid' parameter should not be null or empty")
    }
    if (psk === null) {
        throw new Error("dxNet.netConnectWifiSsid: 'psk' parameter should not be null or empty")
    }
    if (params === null) {
        throw new Error("dxNet.netConnectWifiSsid: 'params' parameter should not be null or empty")
    }
    return netObj.netConnectWifiSsid(ssid, psk, params)
}
/**
 * Get the saved list of hotspots
 * @returns  Saved Hotspot List
 */
net.netGetWifiSavedList = function () {
    return netObj.netGetWifiSavedList()
}
/**
 * Disconnect the currently connected Wi-Fi hotspot
 * @returns  
 */
net.netDisconnetWifi = function () {
    return netObj.netDisconnetWifi()
}
/**
 * Get information on current hot topics
 * @param timeout Required
 * @returns  
 */
net.netGetCurrentWifiInfo = function (timeout) {
    if (timeout === null) {
        throw new Error("dxNet.netGetCurrentWifiInfo: 'timeout' parameter should not be null or empty")
    }
    return netObj.netGetCurrentWifiInfo(timeout)
}

/**
 * Check if the message queue is empty.
 * @returns `true` is empty; `false` is not empty.
 */
net.msgIsEmpty = function () {
    return netObj.msgIsEmpty()
}
/**
 * Retrieve current network status data from the message queue, returning a structure similar to {"type":1,"status":4,"connected":true}.
 * The type references the net.TYPE enumeration.
 * The value of status is explained as follows:
 *  0,    Uninitialized state
    1,    The network card is in the off state.
    2,    The network card is in the enabled state.
    3,    The network cable is plugged in or the Wi-Fi is connected via SSID, but no IP address has been assigned.
    4,    IP address successfully assigned
    5     The specified service has been connected or the test has been passed to connect to the wide area network.
 * @returns   Message data of string type
 */
net.msgReceive = function () {
    let res = JSON.parse(netObj.msgReceive());
    if (res.status >= 4) {
        res.connected = true
    } else {
        res.connected = false
    }
    return res
}

net.STATUS_CHANGE = '__netstatus__changed'

/**
 * Simplify the use of network components; there's no need to poll for network status, as the network status is sent out via eventBus.
 * The `run` command will only be executed once, and the basic network configuration cannot be modified after execution.
 * If you need to obtain real-time network status changes, you can subscribe to eventBus events. The event topic is net.STATUS_CHANGE, and the event content is similar to {"type":1,"status":4,"connected":true}.
 * The type references the net.TYPE enumeration.
 * The value of status is explained as follows:
 *  0,    Uninitialized state
    1,    The network card is in the off state.
    2,    The network card is in the enabled state.
    3,    The network cable is plugged in or the Wi-Fi is connected via SSID, but no IP address has been assigned.
    4,    IP address successfully assigned
    5     The specified service has been connected or the test has been passed to connect to the wide area network.
 * @param {object} options Refer to the options description of init.
 */
net.run = function (options) {
    if (options === undefined || options.length === 0) {
        throw new Error("dxnet.run:'options' parameter should not be null or empty")
    }
    let workerFile = '/app/code/dxmodules/netWorker.js'
    let init = map.get("__net__run_init")
    if (!init) {//Ensure initialization only once
        map.put("__net__run_init", options)
        bus.newWorker('__net', workerFile)
    }
}

/**
 * If the `.net` function is a single thread, you can directly use the `run` function, which will automatically start a thread.
 * If you want to join another existing thread, you can use the following wrapper functions.
 */
net.worker = {
    //Before the while loop
    beforeLoop: function (options) {
        net.init(options)
    },
    //In a while loop
    loop: function () {
        if (!net.msgIsEmpty()) {
            let res = net.msgReceive();
            if (res.status >= 4) {
                res.connected = true
            } else {
                res.connected = false
            }
            bus.fire(net.STATUS_CHANGE, res)
        }
    }
}

export default net;
